package badugaserver;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * @author Wojtek N., Kamil M.
 * @version 0.7
 * WorkerRunnable is a class which communicate between table and client.
 * It is initiated by Listener.class and control basic aspects of the game mechanic.
 */
public class WorkerRunnable extends Thread 
{
	private int allPlayers;
	private int startingTokens;
	private int smallBlind;
	private int bigBlind;
	private int id;
	private int allInRound = 0;
	private int betRound[] = new int[4];
	
	private Socket client;	
	private WorkerRunnable[] sharedWorker;
	private BufferedReader inFromClient = null;
	private PrintWriter outToClient = null;
	
	private String myData = "";
	private String incomingData = "";
	private String playerState = "";
	private String move = "";
	private String[] availableActions;
	
	private boolean isAllIn;
	private boolean playing;
	private boolean continueP = true;
	
	/**
	 * Constructor of the class WorkerRunnable.
	 */
	public WorkerRunnable(int allPlayers, int startingTokens, int smallBlind, int bigBlind, Socket client, PrintWriter outToClient, int playerID, WorkerRunnable[] sharedWorker) 
	{
		this.allPlayers = allPlayers;
		this.startingTokens = startingTokens;
		this.smallBlind = smallBlind;
		this.bigBlind = bigBlind;
		this.client = client;
		this.outToClient = outToClient;
		this.id = playerID;
		this.sharedWorker = sharedWorker;
		setPlayerState(true);
		isAllIn = false;
		for (int i=0; i<betRound.length; i++) 
		{ 
			betRound[i] = 0; 
		}
		
		try 
		{
			inFromClient = new BufferedReader(new InputStreamReader(client.getInputStream()));
		}
		catch (IOException ioe) 
		{
			System.err.println("Failed to create input stream.");
		}
	}
	
	/**
	 * Setter of all available moves and actions able in the game.
	 * @param availableActions[] table of available moves/actions.
	 */
	public void setAvailableActions (String avaliable) 
	{
		int moveID = avaliable.length()/2;
		availableActions = new String[moveID];
		int k = 0;
		for (int i=0; i<moveID; i++) 
		{
			availableActions[i] = avaliable.substring(k, k+2);
			k = k+2;
		}
	}
	
	/**
	 * Checker if action which client want to do is correct. Returns true if is or false if not.
	 * @return boolean (true or false)
	 */
	private boolean isAbleAction (String action)
	{
		for (int i = 0; i < availableActions.length; i++)
		{
			if (action.equals(availableActions[i]))
			{
				return true;
			}
		}
		return false;
	}
	
	/**
	 * @return player move.
	 */
	public String getMove() 
	{
		return move;
	}
	
	/**
	 * @param move setting current move for the player.
	 */
	public void setMove(String move) 
	{
		this.move = move;
	}
	
	/**
	 * @return data
	 */
	public String getData() 
	{
		return myData;
	}
	
	/**
	 * Check if player played All in.
	 * @return the isAllIn
	 */
	public boolean isAllIn() 
	{
		return isAllIn;
	}

	/**
	 * Setter of all in action in round "nr".
	 * @param isAllIn boolean is played all in or not.
	 * @param nr saves round number in which player played all in.
	 */
	public void setAllIn(boolean isAllIn, int nr) 
	{
		this.isAllIn = isAllIn;
		this.allInRound = nr;
	}
	
	/**
	 * @return all in round.
	 */
	public int getallInRound() 
	{
		return allInRound;
	}

	/**
	 * Setter of player state ("wait", "rdy", "nrdy", "reciv").
	 * @param state
	 */
	public void setPlayerState(String state) 
	{
		this.playerState = state;
	}
	
	/**
	 * @return player state.
	 */
	public String checkState() 
	{
		return playerState;
	}
	
	/**
	 * @return initial amount of tokens for player.
	 */
	public int getStartingTokens() 
	{
		return startingTokens;
	}
	
	/**
	 * @return bet did by the player in x round.
	 */
	public int getBetRound(int x) 
	{
		return betRound[x/2-1];
	}

	/**
	 * Setter of last bet in x round.
	 * @param x bet round
	 * @param amount how much was betted by player
	 */
	public void setBetRound(int amount, int x) throws BadugaExceptions
	{
		this.startingTokens = startingTokens - amount;
		if (startingTokens < 0)
		{
			throw new BadugaExceptions(2);
		}
		this.betRound[x/2-1] = betRound[x/2-1] + amount;
	}
	
	/**
	 * @return sum of all bets through the game.
	 */
	public int allBettedMoney() 
	{
		int sum = 0;
		for (int i = 0; i < betRound.length; i++) 
		{
			sum = sum + betRound[i];
		}
		return sum;
	}
	
	/**
	 * @param startingTokens sets new amount of tokens after a round.
	 */
	public void takePrize(int amount) 
	{
		startingTokens = startingTokens + amount;
	}

	/**
	 * @return does player is still playing or left the game.
	 */
	public boolean playing() 
	{
		return playing;
	}

	/**
	 * Sets information, does player still plays (true) or left the game (false)
	 * @param playing 
	 */
	public void setPlayerState(boolean playing) 
	{
		this.playing = playing;
	}
	
	/**
	 * Method which sends information to client.
	 */
	protected void sendToClient(String text) 
	{
		outToClient.println(text);
	}
	
	/**
	 * Restarting data about player to prepare him for next round.
	 */
	public void nextMatch() 
	{
		for (int i=0; i<betRound.length; i++) 
		{ 
			betRound[i] = 0; 
		}
		setPlayerState(true);
		isAllIn = false;
		myData = "";
		playerState = "";
		move = "";
	}
	
	/**
	 * Player wishes to quit after a round.
	 */
	public void playerQuit() 
	{
		continueP = false;
	}
	
	/**
	 * Prints string on server console.
	 */
	private void serverPrint(String s)
	{
		System.out.println(s);
	}
	
	/**
	 * Method prints adequate inscription of action (user friendly string).
	 */
	private String actionPrint(String action)
	{
		if(action.equals("ch"))
		{
			return "check";
		}
		else if(action.equals("be"))
		{
			return "bet";
		}
		else if(action.equals("ra"))
		{
			return "raise";
		}
		else if(action.equals("ca"))
		{
			return "call";
		}
		else if(action.equals("fo"))
		{
			return "fold";
		}
		else if(action.equals("al"))
		{
			return "all-in";
		}	
		return "";
	}

	/*
	 * System of communication:
	 * playerIDall[0]all[1]all[2], where:
	 * playerID takes one space "_" (must be added in message)
	 * all[0] takes 3 spaces "___" (must be added in message)
	 * all[1] takes 2 spaces "__" (must be added in message)
	 * all[2] takes adequate amount of spaces (depends from what was in all[1]; must be at least one sign "_")
	 * Example messages: 
	 *					2DCN___ - means that player 2 has disconnected from the game
	 *					1CHG__TFFT - means that player 1 want to change his first and the last card (during card change phase)
	 *					4AUCra300 - means that player 4 want to raise for 300 (during auction phase)
	 * Below is given full table of possible inserts.
	 */
	@Override
	public void run() 
	{
		try 
		{
			serverPrint("Player " + id + " enters table.");
			synchronized(this) 
			{
				for (int i=0; i<sharedWorker.length; i++) 
				{
					if (sharedWorker[i]!=null && sharedWorker[i]!=this) 
					{
						sharedWorker[i].outToClient.println(allPlayers + "W8T__" + id + (allPlayers-(id+1)));
					}
				}
			}			
			
			/*
			 * all[0] = info - says what is currently happening in the game
			 *					INI - initialization of program					
			 *					BEG - starting parameters for players
			 *					W8T - waiting for missing players
			 *					DBS - setting dealer (D), big blind (B) and small blind (S)
			 *					RDY - player is asked are we ready to move on
			 *					AUC - player wants to bet (auction phase)
			 *					CHG - player changing cards (changing cards phase)
			 *					NXT - player have been asked does he wish to continue the game
			 *					WON - player have won
			 *					DCN - player disconnected
			 *					ERR - something gone wrong
			 * all[1] = action - says what player wants to or may do
			 * 					ur - server informs, that player may do his move
			 * 		d	b	->	al - All in
			 *		u	e	->	be - Bet
			 *		r	t	->	ca - Call
			 *		i	t	->	ch - Check
			 *		n	i	->	fo - Fold
			 *		g	n	->	ra - Raise
			 *					__ - player should wait for his move (during CHG or just empty space - no action)
			 *					os - players turn to exchange cards (during CHG)
			 *					cd - server sends new hand to the player
			 *					on - in phase 9 - means there is only one winner
			 *					dr - in phase 9 - means there is a draw
			 * all[2] = content - says for example how much player wants to bet or which cards exchange
			 * 					digits (as big as it is needed by program)
			 * 					YN - while asking do players wish to continue
			 * 					PR - player have lost the game (lost all tokens)
			 * 					ED - ended game (some players decided to quit)
			 * 					
			 */
			String[] all = new String[3];
			
			while (incomingData!=null && continueP) 
			{
				incomingData = inFromClient.readLine();
				
				if (incomingData != null) 
				{
					serverPrint("Player " + id + " last move: " + incomingData + " and his state: " + playerState);
					all[0] = incomingData.substring(1, 4);
					if (all[0].equals("DCN")) 
					{
						serverPrint("Player " + id + " disconnected.");
						break;
					}
					else 
					{
						if (playerState == "wait") 
						{
							if (all[0].equals("RDY")) 
							{
								serverPrint("Player " + id + " is ready.");
								setPlayerState("rdy");
							}
							else if (all[0].equals("AUC")) 
							{
								myData = "";
								all[1] = incomingData.substring(4,6);
								if (isAbleAction(all[1])) 
								{
									all[2] = incomingData.substring(6, incomingData.length());
									serverPrint("Player " + id + " wants to " + actionPrint(all[1]) + " with " + all[2] + " tokens");
									setMove(all[1]);
									myData = all[2];
									setPlayerState("reciv");
								}
								else 
								{
									serverPrint("Player " + id + " wants to " + actionPrint(all[1]) + ", but he cannot do that! (Probably not enough tokens)");
									outToClient.println(id+"ERRbe You do not have enough tokens!");
									setPlayerState("wait");
								}
							}
							else if (all[0].equals("CHG")) 
							{
								all[2] = incomingData.substring(6, incomingData.length());
								myData = "";
								if(all[2].length() == 4)
								{
									setMove(all[2]);
									myData += incomingData.substring(6, incomingData.length());
									setPlayerState("reciv");
								}
								else 
								{
									outToClient.println(allPlayers + "ERR__" + id);
									setPlayerState("wait");
								}
							}
							else if (all[0].equals("NXT")) 
							{
								myData = incomingData.substring(6, incomingData.length());
								setPlayerState("rdy");
							}
							
						}
					}
				}
			}
			if (continueP) 
			{
				serverPrint("Player " + id + " disconnected; thread quits.");
			}
			else 
			{
				serverPrint("Player " + id + " decided to leave; thread quits.");
			}
			
			inFromClient.close();
			outToClient.close();
			client.close();
		}
		catch (IOException ioe) 
		{
			System.err.println("Something have gone wrong. Closing up.");
		}
	}

}
